/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package tools;

import com.openbrew.openbrewrf.Administration;
import com.openbrew.openbrewrf.DiscountCode;
import com.openbrew.openbrewrf.FinishedGoodsInventory;
import com.openbrew.openbrewrf.InvPSOLines;
import com.openbrew.openbrewrf.InvSalesLines;
import com.openbrew.openbrewrf.PSOLines;
import com.openbrew.openbrewrf.PendingSO;
import com.openbrew.openbrewrf.Sales;
import com.openbrew.openbrewrf.SalesOrder;
import com.openbrew.openbrewrf.views.POSBeerView;
import emailer.SalesOrderEmailer;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import tools.model.PSOFullfillment;

/**
 *
 * @author chapmand
 */
public class PendingSOTools {
    /***************************************************************************
     * Pending Sales Orders Tools.
     **************************************************************************/  
    /***************************************************************************
     * doPartialFullfillment - full fills a partial PSO
     * @param en - Entity Manager
     * @param pso - Pending Sales Order Item
     * @return 
     **************************************************************************/
    public PSOFullfillment doPartialFullfillment(final EntityManager en, final PendingSO pso){
        //Get administration
        final Administration admin = getAdmin(en);
        //Initialize new lists.
        List<PSOLines> psot = getRemainingPSOs(pso);
        List<PSOLines> filledItems = getFilledPSOs(pso);
        List<InvPSOLines> ipsot = getRemainingInventoryPSOs(pso);
        List<InvPSOLines> filledInvItems = getFilledInventoryPSOs(pso);
        //Get todays date.
        final java.util.Date moveDate = new java.util.Date();

        List<Sales> soList = new ArrayList<Sales>();
        List<InvSalesLines> isoList = new ArrayList<InvSalesLines>();
        pso.setPSODate(moveDate);
        String newRemainderXML = "";
        String newFilledXML = "";

        String kegList = "";

        if(psot != null || ipsot != null){
            //create a new pending sales order for said stuff.
            PendingSO newPSO = new PendingSO();
            newPSO.setPSOComplete(false);
            newPSO.setPSOCustID(pso.getPSOCustID());
            newPSO.setPSODate(pso.getPSODate());
            newPSO.setPSOAllocationID(pso.getPSOAllocationID());
            newPSO.setPsoxml(newRemainderXML);
            newPSO.setPSOLinesCollection(psot);
            newPSO.setInvPSOLinesCollection(ipsot);
            createPendingSO(en, newPSO);
        }

        //Filled items need to be updated.
        if(filledItems != null || filledInvItems != null){
            //Run on filled items only.
            if(filledItems != null){
                for(final PSOLines psoLine : filledItems){
                    //Set customer to keg.
                    int kegID = 0;
                    if(psoLine.getPSOKeg()!=0){
                        kegID = psoLine.getPSOKeg();
                        kegList += psoLine.getPSOFGI().getFGIDescription() + " - " 
                                + String.format(admin.getKegPrefix()+"%08d", psoLine.getPSOKeg()) + "\n";
                    }
                    //Add sales line to database.
                    soList.add(insertSalesLine(en, psoLine, pso, kegID));

                    if(psoLine.getPSOQtyLeft() > 0){
                        psoLine.setPSOQty(psoLine.getPSOQtyLeft());
                    }
                    updatePSOLines(en, psoLine);
                }
            }
            if(filledInvItems != null){
                for(final InvPSOLines ipsoLine : filledInvItems){
                    //Add sales line to database.
                    isoList.add(insertInventorySalesLine(ipsoLine, pso));

                    if(ipsoLine.getInvPSOQtyLeft() > 0){
                        ipsoLine.setInvPSOQty(ipsoLine.getInvPSOQtyLeft());
                    }
                    updateInvPSOLines(en, ipsoLine);
                }
            }

            //Now add the sales order line to the database.
            final SalesOrder soNum = insertSalesOrderLine(en, pso, soList, isoList);

            pso.setPsoxml(newFilledXML);
            pso.setPSOComplete(true);
            updatePSO(en, pso);
            
            if(admin.getInvEmail()){
                //If it isn't a distribution company, and send email is enabled.  
                if(!soNum.getSOCust().getCEmail().equals("")){
                    SalesOrderEmailer salesOrderEmailer = new SalesOrderEmailer();
                    salesOrderEmailer.sendMail(en, soNum);
                } 
            }
            
            return new PSOFullfillment(kegList, soNum);
        }
        
        return null;
    }
    
    /***************************************************************************
     * doPartialFullfillment - full fills a partial PSO
     * @param en - Entity Manager
     * @param pso - Pending Sales Order Item
     * @return 
     **************************************************************************/
    public PSOFullfillment doFullFullfillment(final EntityManager en, final PendingSO pso){
        //Get administration
        final Administration admin = getAdmin(en);
        //Initialize new lists.
        //Get todays date.
        final java.util.Date moveDate = new java.util.Date();
        List<Sales> soList = new ArrayList<Sales>();
        List<InvSalesLines> isoList = new ArrayList<InvSalesLines>();

        pso.setPSODate(moveDate);

        String kegList = "";

        if(pso.getPSOLinesCollection() != null){
            for(final PSOLines psoLine : pso.getPSOLinesCollection()){
                //Set customer to keg.
                int kegID = 0;
                if(psoLine.getPSOKeg()!=0){
                    kegID = psoLine.getPSOKeg();
                    kegList += "\n " + psoLine.getPSOFGI().getFGIDescription() + " - " 
                            + String.format(admin.getKegPrefix()+"%8d", psoLine.getPSOKeg()) + "\n";
                }
                //Add sales line to database.
                soList.add(insertSalesLine(en, psoLine, pso, kegID));
            }
        }
        if(pso.getInvPSOLinesCollection() != null){
            for(final InvPSOLines ipsoLine : pso.getInvPSOLinesCollection()){
                //Add sales line to database.
                isoList.add(insertInventorySalesLine(ipsoLine, pso));
            }
        }

        //Now add the sales order line to the database.
        final SalesOrder soNum = insertSalesOrderLine(en, pso, soList, isoList);

        pso.setPsoxml("");
        pso.setPSOComplete(true);
        updatePSO(en, pso);
            
        if(admin.getInvEmail()){
            //If it isn't a distribution company, and send email is enabled.  
            if(!soNum.getSOCust().getCEmail().equals("")){
                SalesOrderEmailer salesOrderEmailer = new SalesOrderEmailer();
                salesOrderEmailer.sendMail(en, soNum);
            } 
        }
            
        return new PSOFullfillment(kegList, soNum);
    }
    
    private List<PSOLines> getRemainingPSOs(PendingSO pso){
        boolean itemRemaining = false;
        List<PSOLines> newLines = new ArrayList<PSOLines>();
        if(pso.getPSOLinesCollection() != null){
            for(PSOLines psoLine : pso.getPSOLinesCollection()){
                if(!psoLine.getPSOSold()){
                    if(psoLine.getPSOQtyLeft() > 0){
                        newLines.add(psoLine);
                        itemRemaining = true;
                    }
                }
            }
        }
        if(itemRemaining){
            return newLines;
        } else {
            return null;
        }
    }
    
    private List<PSOLines> getFilledPSOs(PendingSO pso){
        boolean itemFilled = false;
        List<PSOLines> newLines = new ArrayList<PSOLines>();
        if(pso.getPSOLinesCollection() != null){
            for(PSOLines psoLine : pso.getPSOLinesCollection()){
                if(psoLine.getPSOQtyLeft() < psoLine.getPSOQty()){
                    newLines.add(psoLine);
                    itemFilled = true;
                }
            }
        }
        if(itemFilled){
            return newLines;
        } else {
            return null;
        }
    }
    
    private List<InvPSOLines> getRemainingInventoryPSOs(PendingSO pso){
        boolean itemRemaining = false;
        List<InvPSOLines> newLines = new ArrayList<InvPSOLines>();
        if(pso.getInvPSOLinesCollection() != null){
            for(InvPSOLines ipsoLine : pso.getInvPSOLinesCollection()){
                if(!ipsoLine.isInvPSOSold()){
                    if(ipsoLine.getInvPSOQtyLeft() > 0){
                        newLines.add(ipsoLine);
                        itemRemaining = true;
                    }
                }
            }
        }
        if(itemRemaining){
            return newLines;
        } else {
            return null;
        }
    }
    
    private List<InvPSOLines> getFilledInventoryPSOs(PendingSO pso){
        boolean itemFilled = false;
        List<InvPSOLines> newLines = new ArrayList<InvPSOLines>();
        if(pso.getInvPSOLinesCollection() != null){
            for(InvPSOLines ipsoLine : pso.getInvPSOLinesCollection()){
                if(ipsoLine.getInvPSOQtyLeft() < ipsoLine.getInvPSOQty()){
                    newLines.add(ipsoLine);
                    itemFilled = true;
                }
            }
        }
        if(itemFilled){
            return newLines;
        } else {
            return null;
        }
    }
    
    private Sales insertSalesLine(final EntityManager en, final PSOLines psoLine, final PendingSO pso, final int kegID){
        final String SNotes = "Outside SO added via RF Partial Fullfill";
        final float price = psoLine.getPSOPriceOverRide();
        
        POSBeerView beer = new POSBeerView();
        beer.setbId(psoLine.getPSOFGI().getFGIBeerID().getbId());
        beer.setbName(psoLine.getPSOFGI().getFGIBeerID().getbName());
        Sales sale = new Sales();
        sale.setCustID(pso.getPSOCustID());
        sale.setSDate(pso.getPSODate());
        sale.setSNotes(SNotes);
        DiscountCode dc = psoLine.getPSODiscCode();
        final FinishedGoodsInventory fgi = getFGI(en, psoLine.getPSOFGI().getFgiid());
        sale.setSDisc(dc);
        sale.setSalesFGIID(fgi);
        sale.setSalesFGIQty(psoLine.getPSOQty() - psoLine.getPSOQtyLeft());
        sale.setUnitPrice(fgi.getPrice(pso.getPSOCustID().getCId()));
        sale.setServingTankVolume(0);
        sale.setKegID(kegID);
        sale.setPriceOver(price);
        return sale;
    }
    
    private InvSalesLines insertInventorySalesLine(final InvPSOLines ipsoLine, final PendingSO pso){
        InvSalesLines isl = new InvSalesLines();
        isl.setInvSaleCustID(pso.getPSOCustID());
        isl.setInvSaleDate(pso.getPSODate());
        isl.setInvSaleDisc(ipsoLine.getInvPSOdisc());
        isl.setInvSaleSupplyID(ipsoLine.getInvPSOSupplyID());
        isl.setInvSalePriceOver(ipsoLine.getInvPSOPriceOver());
        isl.setInvSaleQty(ipsoLine.getInvPSOQty() - ipsoLine.getInvPSOQtyLeft());
        isl.setiSaleUnitPrice(ipsoLine.getInvPSOSupplyID().getSupWholeSalePrice());
        return isl;
    }
    
    private SalesOrder insertSalesOrderLine(final EntityManager en, final PendingSO pso, final List<Sales> soList, final List<InvSalesLines> isoList){
        SalesOrder so = new SalesOrder();
        so.setSODate(pso.getPSODate());
        so.setSOPaid(false);
        so.setSOCust(pso.getPSOCustID());
        so.setCheckNo(0);
        so.setCheckNotes("");
        so.setSalesCollection(soList);
        so.setInvSalesLinesCollection(isoList);
        createSalesOrderWithAllocations(en, so);
        return so;
    }
    
    /***************************************************************************
     * Database modification functions.
     **************************************************************************/
    /***************************************************************************
     * createPendingSO - creates the pending so and creates inventories.
     * @param en
     * @param pso
     * @return 
     **************************************************************************/
    public PendingSO createPendingSO(final EntityManager en, final PendingSO pso){
        final InventoryModifiers im = new InventoryModifiers();
        if(pso.getPSOLinesCollection()!=null){
            for(PSOLines psoLine : pso.getPSOLinesCollection()){
                if(psoLine.getPSOID() == null || psoLine.getPSOID() == 0){
                    im.doFGIAllocationAddition(en, psoLine.getPSOFGI().getFgiid()
                            , psoLine.getPSOQty());
                }
            }
        }
        if(pso.getInvPSOLinesCollection()!=null){
            for(InvPSOLines psoLine : pso.getInvPSOLinesCollection()){
                if(psoLine.getInvPSOID() == null || psoLine.getInvPSOID() == 0){
                    im.doSupplyAllocationAddition(en, psoLine.getInvPSOSupplyID().getSupID()
                            , psoLine.getInvPSOQty());
                }
            }
        }
        en.persist(pso);
        en.flush();
        en.refresh(pso);
        return pso;
    }
    
    private void updatePSOLines(final EntityManager en, final PSOLines psoLine){
        final InventoryModifiers im = new InventoryModifiers();
        PSOLines oldLine = en.find(PSOLines.class, psoLine.getPSOID());
        final int oldQty = oldLine.getPSOQty();
        en.merge(psoLine);
        final int newQty = psoLine.getPSOQty();
        if((oldQty - newQty)>0){
            im.doFGIAllocationSubtraction(en, (int)oldLine.getPSOFGI().getFgiid()
                    , oldQty - newQty);
        } else {
            im.doFGIAllocationAddition(en, (int)oldLine.getPSOFGI().getFgiid()
                    , newQty - oldQty);
        }
    }
    
    private void updateInvPSOLines(final EntityManager en, final InvPSOLines ipsoLine){
        final InventoryModifiers im = new InventoryModifiers();
        InvPSOLines oldLine = en.find(InvPSOLines.class, ipsoLine.getInvPSOID());
        final int oldQty = oldLine.getInvPSOQty();
        en.merge(ipsoLine);
        final int newQty = ipsoLine.getInvPSOQty();
        if((oldQty - newQty)>0){
            im.doSupplyAllocationSubtraction(en, oldLine.getInvPSOSupplyID().getSupID()
                    , oldQty - newQty);
        } else {
            im.doSupplyAllocationAddition(en, oldLine.getInvPSOSupplyID().getSupID()
                    , newQty - oldQty);
        }
    }
    
    private SalesOrder createSalesOrderWithAllocations(final EntityManager en, final SalesOrder so){
        final SaleTools st = new SaleTools();
        final SalesOrder newSO = st.doSaleOrderInsertionActions(en, so, true);
        if(newSO != null){
            en.persist(so);
            en.flush();
            en.refresh(so);
            return so;
        } 
        return null;
    }
    
    private void updatePSO(final EntityManager en, final PendingSO pso){
        final SaleTools st = new SaleTools();
        st.fgiAllocationUpdatePerPSO(en, pso);
        st.supplyAllocationUpdatePerPSO(en, pso);
        en.merge(pso);
    }
    
    private Administration getAdmin(final EntityManager en){
        TypedQuery<Administration> query = en.createQuery(
            "SELECT a FROM Administration a WHERE a.aNum = '1'", Administration.class);
        Administration admin = query.getSingleResult();
        return admin;
    }
    
    private FinishedGoodsInventory getFGI(final EntityManager en, final int fgiID){
        TypedQuery<FinishedGoodsInventory> query = en.createQuery(
            "SELECT f FROM FinishedGoodsInventory f WHERE f.fgiid = ?1", FinishedGoodsInventory.class);
        query.setParameter(1, fgiID);
        FinishedGoodsInventory fgi = query.getSingleResult();
        return fgi;
    }
}
